package chapter4;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamDemo1 {
    /**
     * 流时什么？
     *      流是Java API的新成员 它允许你以声明性方式处理数据集合(通过查询语句来表达，而不是临时编写一个实现).此外流还能透明低并行处理
     *      因此无需写任何多线程代码
     */

    public static void main(String[] args) {
        List<Dish> lowCaloricDishes = new ArrayList<>();
        List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH) );
        //1、用累加器筛选元素
        for(Dish d: menu){
            if (d.getCalories() < 400){
                lowCaloricDishes.add(d);
            }
        }

        //2、用匿名类对菜肴排序
        Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
            @Override
            public int compare(Dish o1, Dish o2) {
                return Integer.compare(o1.getCalories(), o2.getCalories());
            }
        });

        //3、处理排序后菜单名
        List<String> lowCaloricDishesName = new ArrayList<>();
        for(Dish d: lowCaloricDishes){
            lowCaloricDishesName.add(d.getName());
        }

        /**
         * 使用流处理的方式
         * 如果需要利用多核架构 执行这段代码只要把stream()换成 parallelStream()
         */
        List<String> lowCaloricDashesName2 =
                menu.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(Collectors.toList());

        /**
         * 流简介
         */
        List<String> threeHighCaloricDishNames =
                //从menu中获取流 建立操作流水线
                menu.stream()
                        // 首先选出高热量的菜肴
                .filter(d -> d.getCalories() > 300)
                        //获取菜名
                .map(Dish::getName)
                        //只选择头3个
                .limit(3)
                        //将结果保存在另一个List中
                .collect(Collectors.toList());
        System.out.println(threeHighCaloricDishNames);

        /**
         * 流只能遍历一次
         */
        List<String> title = Arrays.asList("Java8", "In", "Action");
        Stream<String> s = title.stream();
        s.forEach(System.out::println);
        /**
         * 抛出异常： 流已被操作或关闭
         */
//        s.forEach(System.out::println);

        /**
         * 内部迭代与外部迭代
         *  使用Collection接口需要用户去做迭代(例如for-each)这称为外部迭代
         *  相反，Stream库使用内部迭代---它帮我们把迭代做了  还把得到的流值存在了某个地方 我们只要给出一个函数说要干什么就可以了
         */

        /**
         * 流操作
         * java.util.stream.Stream中的Stream定义了很多操作 分为2大类：中间操作和终端操作
         *
         * 中间操作：
         *      诸如filter和sorted等中间操作 会返回另一个流 可以多个操作连接起来形成一个查询
         *      除非流水线上触发一个终端操作 否则中间操作不会执行任何处理 在终端操作时一次性全部处理
         *
         * 终端操作
         *      终端操作会从流的流水线生成结果
         *
         * 使用流：
         *      1、一个数据源(如集合) 来执行一个查询
         *      2、一个中间操作链 形成一条流的流水线
         *      3、一个终端操作 执行流水线 并能生成结果
         *
         * 流的筛选：
         *      distinct---筛选不同的元素
         *      limit(n)------返回一个不超过给定长度的流
         *      skip(n)-------返回一个扔掉前n个元素的流
         *
         * 映射：
         *      map---接收一个函数作为参数  这个函数会应用于每个元素上 并将其映射成一个新的元素
         *      flatmap ---流的扁平化 它将一个流中的每个值都换成另一个流 然后把所有的流连接起来成为一个流
         *
         * 查找和匹配：
         *      anyMatch----至少匹配一个元素
         *      allMatch----匹配所有元素
         *      noneMatch---流中没有任何匹配的元素
         *      findAny--  返回当前流中任意元素
         *      Optional---  Optional<T>是一个容器类 代表一个值存在或不存在
         *      findFirst---查找第一个元素
         *
         * 规约：
         *      reduce-- 它接收2个参数 一个是初始值  一个是BinaryOperator<T> 来将两个元素结合起来产生一个新值
         *
         * 数值流：
         *      原始类型流特化： IntStream、DoubleStream和LongStream
         *      转换会对象流  使用boxed()
         *      数值范围：IntStream和LongStream有2个静态方法 range和rangeClosed 2个参数分别代表起始值和结束值
         *      IntStream.range(1, 100)  IntStream.rangeClosed(1,100)
         *
         * 构建流：
         *      由值创建流 使用静态方法 Stream.of()
         *          Stream<String> stream = Stream.of("Java 8", "Lambda", "in action");
         *      由数组创建流
         *          int[] numbers = {2, 3, 5, 7, 11, 13};
         *          int sum = Arrays.stream(numbers).sum();
         *      由文件生成流
         *          Java中用于处理文件等I/O操作的NIO API已更新，以便利用Stream API.
         *          java.nio.file.Files中很多静态方法都会返回一个流
         *      由函数生成流：创建无限流
         *          Stream API提供了两个静态方法来从函数生成流：Stream.iterate和Stream.generate.这两个操作可以创建所谓的无限流：不像从固定集合创建的流那样有固定大小的流
         *          Stream.iterate(0, n -> n + 2)
         *              .limit(10)
         *              .forEach(System.out::println);
         *               生成：
         *                 与iterate方法类似 generate方法也可以按需生成一个无限流 但generate不是一次对每个新生成的值应用函数  它接受一个Supplier<T>类型的Lambda提供新的值
         *
         *
         */

        Stream.iterate(new int[]{0,1}, fibo -> new int[]{fibo[1],fibo[0] + fibo[1]})
                .limit(20)
                .forEach(t -> System.out.println("(" + t[0] + "," + t[1] + ")"));

        Stream.generate(Math::random)
                .limit(5)
                .forEach(System.out::println);


    }
}
